正则表达式及 grep sed awk 工具
Table of Contents
正则表达式
元字符
\b, \B, ^, $
\b 匹配单词的开头或结尾, 即单词的分界处, 英文的单词通常用空格, 标点符号和换来来分隔, 但 \b 不匹配这些, 它只匹配一个位置.
\B 匹配不是单词开头或结束的位置.
如, 想在一段文本中, 查找 'hi' 这个单词, 一般会把 history 这种单词也匹配进来, 如果使用 \bhi\b, 就可以只匹配 hi 这个单词.
^ 和 $ 也匹配一个位置, ^ 匹配要查找的字符串的开头, $ 匹配结尾. 如果有设置处理多行, 则 ^ 和 $ 也表示一行的开始处和结尾处.
如, 要输入 QQ 号码, QQ 号码有 5-12 位, 可以使用 ^\d{5,12}$
.
. 匹配 除换行符以外
的任何字符.
表示重复的元字符: *, +, ?, {n}, {n,}, {n,m}
- 匹配的是数量. 表示 * 前边的内容可以连续重复使用任意次以使整个表达式得到匹配.
所以, .* 表示任意数量的不包含换行的字符. \bhi\b.*Lucy\b: 先是一个单词 hi, 然后是任意个字符, 但不能是换行, 最后是 Lucy.
- 也匹配数量, 但 * 可以匹配任意次(包括 0 次), 而 + 则匹配重复 1 次或更多次.
? 重复 0 次或 1 次.
{n} 重复 n 次.
{n,} 重复 n 次或更多次.
{n,m} 重复 n 到 m 次.
\d, \s, \w, \D, \ S, \W
\d 匹配一位数字, 等价于 [0-9]. 如果想匹配 3 位连续的数字, 可以写成: \d\d\d, 也可以写成: \d{3}
\s 匹配任意的空白符, 包括空格, Tab, 换行符, 中文全角空格等.
\w 匹配字母, 数字, 下划线. 等价于 '[A-Za-z0-9_]'
例子: \ba\w*\b, 匹配任意以字母 a 开头的单词, 首先是单词开始处 \b, 然后是字母 a, 然后是任意数量的字母或数字 \w*, 最后是单词的结束 \b.
最后, \D, \ S, \W 都是 \d, \s, \w 的反义词, 如, \D 表示任意非数字的字符.
\
\ 表示转义符, 比如想查找 . 或 *, 由于它们是元字符, 会被解释成其他意义. \. 可以表示字符 ., 如果想查找 \ 本身, 就使用 \\.
方括号 []
[] 里面写的内容, 可以匹配 一个字符
.
[aeiou] 可以匹配 a 或 e 或 i 或 o 或 u 这五个字符中的一个;
[.?!] 可以匹配标点符号 . 或 ? 或 !
\(?0\d{2}[) -]?\d{8} 可以匹配几种格式的电话号码, 如 (010)88886666, 022-22334455, 02912345678. 首先是转义字符 \(, ? 表示 \( 可以出现 0 次或 1 次; 然后是数字 0; 然后是任意的两个数字 \d{2}, 然后是 ) 或 - 或空格 [) -], 可以出现 0 次或 1 次; 最后是 8 个数字 \d{8}.
在 [] 内部使用 ^ 表示取反
, 如: [^aeiou] 表示除 aeiou 这五个字母之外的任意字符.
分枝条件
\(?0\d{2}[) -]?\d{8} 也可以匹配到 (010-87654321 这种不正确的格式.
分枝条件是指, 在正则表达式中写好几种规则, 满足其中一种规则就可以匹配, 几种规则使用 | 分隔.
如: 0\d{2}-\d{8}|0\d{3}-\d{7} 可以匹配两种以 - 分隔的电话号码, 一种是三位区号, 8 位本地号, 另一种是 4 位区号, 7 位本地号.
\d{5}-\d{4}|\d{5} 可以匹配美国的邮政编码. 美国邮编的规则是 5 位数字, 或者用 - 连接的 9 位数字. 但不能改成 \d{5}|\d{5}-\d{4}, 因为 使用分枝条件时, 会从左到右测试每个条件, 如果满足了某个分枝, 就不会再去管其他的条件了
. 所以这边, 只会用到前一个分枝, 只能匹配 5 位的邮编, 或者 9 位邮编中的前 5 位.
分组
() 可以用来指定子表达式(也叫分组). 如果想要重复多个字符, 就可以用小括号来指定子表达式, 然后指定这个子表达式的重复次数.
匹配 IP 的简单表达式: (\d{1,3}\.){3}\d{1,3} 解析: \d{1,3} 匹配 1 到 3 位数字, (\d{1,3}\.){3} 匹配三位数字加上一个英文句号, 然后这个整体重复 3 次, 最后再加上一个 1 到 3 位的数字 \d{1,3}
但是, 这个表达式还可以匹配 256.300.999.888 这样的不可能存在的 IP, 所以要改成: ((2[0-4]\d|25[0-5]|1?\d\d?\.){3}(2[0-4]\d|25[0-5]|1?\d\d?)
后向引用
后向引用是指, 前一个子表达式匹配的内容, 可以传递给后一个子表达式作进一步处理. 以分组的左括号为标志, 第一个出现的分组组号为 1, 写成 \1, 第二个出现的分组组号为 2, 写成 \2.
\b(\w+)\b\s+\1\b 的作用是匹配重复出现的单词, 比如 go go. 这个表达式首先是一个词, 这个词有一或多个的字母, 数字或下划线 \b(\w+)\b. 这个词会被捕获到编号为 1 的分组中, 然后是 1 个或几个空白符 (\s+), 最后是分组 1 中捕获的内容 \1.
指定分组名字
我们可以自己指定分组的名字, 语法如下:
(?<name>exp) # 要使用时 \k<name>
上面的例子也可以写成: \b(?<word>\w+)\b\s+\k<word>\b
分组的小结
(exp): 匹配 exp, 并捕获文本到自动命名的组里
(?<name>exp): 匹配 exp, 并捕获文本到名称为 name 的分组中, 也可以写成: (?'name'exp)
(?:exp): 匹配 exp, 但不捕获匹配的广西, 也不给此分组分配组号
(?=exp): 匹配 exp 前面的位置
(?<=exp): 匹配 exp 后面的位置
(?!exp): 匹配后面跟的不是 exp 的位置
(?<!exp): 匹配前面不是 exp 的位置
(?#comment): 注释
零宽断言
断方用来声明一个应该为真的事实. 正则表达式中, 只有当断言为真时, 才会继续进行匹配.
零宽度正预测先行断言 (?=exp)
(?=exp) 断言自身出现的位置的后面能匹配表达式 exp. 如: \b\w+(?=ing\b) 表示匹配以 ing 结尾的单词的前面部分, 当文本为 I'm singing while you're dancing 时, 可以匹配到 sing 和 danc.
零宽度正回顾后发断言 (?<=exp)
(?<=exp) 断言自身出现的位置的前面能匹配表达式 exp. 如: (?<=\bre)\w+\b 能匹配以 re 开头的单词的后半部分, 当文本为 reading a book 时, 它匹配 ading.
零宽度负预测先行断言 (?!exp)
(?!exp) 断言此位置的后面不能匹配表达式 exp.
如, \d{3}(?!\d) 匹配三位数字, 而且这三位数字的后面不能是数字.
如, \b((?!abc)\w)+\b 匹配不包含连续字符串 abc 的单词.
零宽度负回顾后发断言 (?<!exp)
(?<!exp) 断言此位置的前面不能匹配表达式 exp.
如, (?<![a-z])\d{7} 匹配前面不是小写字母的 7 位数字.
复杂的例子
(?<=<(\w+)>).*(?=<\/\1>) 匹配 HTML 标签内的内容.
<(\w+)>: 匹配被 <> 括起来的单词, 包括 <>, 用 \1 来表示.
(?<=<(\w+)>): 匹配 <(\w+)> 后面的部分, 不包括 <(\w+)>
.*: 匹配任意长度的字符.
<\/\1>: \1 表示分组 1, 在前面匹配到的, \/ 中, \ 是转义符, 最终表示字符 /, 所以假如 \1 为 html, 则相当于 </html>
(?=<\/\1>): 匹配 <\/\1> 前面的部分, 不包括 <\/\1>
懒惰匹配
正则表达式默认是贪婪匹配. 如果要改成懒惰匹配, 可以在限定符后面加 ?
*? : 重复任意次, 但尽可能少重复 +? : 重复 1 次或更多次, 但尽可能少重复 ?? : 重复 0 次或 1 次, 但尽可能少重复 {n,m}? : 重复 n 到 m 次, 但尽可能少重复 {n,}? : 重复 n 次以上, 但尽可能少重复
grep
语法
grep 用来一行一行分析数据. 其语法如下:
grep [-a,-c,-i,-n,-v] [--color=auto] '查找字符串' filename -a: 将 binary 文件以 text 文件的方式查找数据; -c: 计算 '查找字符串' 出现的次数; -i: 忽略大小写; -n: 输出行号; -v: 反向选择, 即找出不包含 '查找字符串' 的那一行 --color=auto: 将 '查找字符串' 加上颜色显示
基本用法
last | grep 'pinvon' # 将 last 当中, pinvon 用户登录的那些行显示. last | grep -v 'pinvon' # 将 last 当中, 不是 pinvon 用户登录的那些行显示 grep --color=auto 'MANPATH' /etc/man.config # 将 /etc/man.config 内包含 MANPATH 的那些行显示
如果想每次使用 grep 时, 相关信息都用颜色表示, 就用 alias 设置别名:
alias grep='grep --color=auto'
配合正则表达式
grep -n 'o\{2\}' regular_express.txt # 在 regular_express.txt 中找到有两个 o 的行 ls | grep -n '^a.*' # 以 a 开头的文件名
sed
sed 是一个管道命令, 可以利用 script 来处理文本文件.
基本语法:
sed [-nefr] [动作] -n: 使用安静模式, 仅显示被处理后的结果 -e: 在命令行模式中进行 sed 的动作编辑 -f: 直接将 sed 的动作写在一个文件内, -f filename 则可以执行 filename 内的 sed 动作 -r: 使用正则表达式 -i: 直接修改读取的文件内容, 而不是由屏幕输出 # 动作说明: [n1[,n2]]function [n1[,n2]]: 可选, 表示要处理的行数. # function 有下面这些参数 a: 新增, 后接要增加的字符串 c: 替换, 后接用于替换的字符串 d: 删除 i: 插入, 后接要插入的字符串 p: 打印, 将某个选择的数据打印出来 s: 替换, 可以搭配正则表达式 sed 's/要被替换的字符串/新的字符串/g'
使用
- 将 filename 里面的内容列出并打印行号, 同时, 将第 2~5 行删除
nl filename | sed -e '2,5d'
由于 -e 的意思是在命令行上进行 sed 的编辑动作, 所以对 filename 这个文件本身不产生影响. -e 是默认的, 可不加.
- 将 filename 里面的内容列出并打印行号, 同时, 在第 2 行后面增加两行字
nl filename | sed '2a add line 1 \nadd line 2'
如果要在第 2 行之前插入数据, 则把 a 改成 i.
- 将 filename 里面的内容列出并打印行号, 同时, 将第 2~5 行的内容替换
nl filename | sed '2,5c test'
- 打印 filename 里面的 第 2~5 行内容
nl filename | sed -n '2,5p'
这边使用 -n, 是因为如果不加, 会把所有的内容都输出, 并且第 2~5 行的内容会输出两次. 而加上 -n 后, 只输出第 2~5 行的内容, 其他内容不输出.
- 假设 filename 中的注释符号是 #, 使用 sed 命令删除 filename 中的所有注释
cat filename | sed 's/#.*$//g'
awk
awk 和 sed 类似, 也可以处理文件. sed 常把一行当作一个单元来处理, 而 awk 则把一行分成几个字段, 对字段进行处理.
语法如下:
awk '条件类型 1{动作 1} 条件类型 2{动作 2} ...' filename
默认的字段分隔符是空格或 Tab.
实例
使用 last 可以将登录者的数据取出来, 如果只想取出其中的账号和 IP, 且账号与 IP 之间以 Tab 隔开, 则可以这样:
last -n 5 -i | awk '{print $1 "\t" $3}'
awk 有几个内置变量, NF 表示每行拥有的字段总数, NR 表示当前处理的是第几行, FS 表示默认的分隔符是什么.
last -n 5 -i | awk '{print $1 "\t lines: " NR "\t columns: " NF}'
查看第 3 列小于 10 的数据:
cat filename | awk '$3 < 10 {print $1 "\t" $3}'
设置其他的分隔符:
cat filename | awk 'BEGIN {FS=";"} $3 < 10 {print $1 "\t" $3}'
awk 的动作内部, 也支持 if 语句, 如:
cat filename | awk '{if ($3 < 10) {print $1 "\t" $3}'
Footnotes:
DEFINITION NOT FOUND.
Generated by Emacs 25.x(Org mode 8.x)
Copyright © 2014 - Pinvon - Powered by EGO